﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.TV2.Tools;
using UDS.DB.Lib;

namespace UDS.DB.SP1
{
    internal class Installer
    {
        private const string BaseDir = "UDS.DB.SP1.";

        internal static void Main(string[] args)
        {
            var cmdArgs = new CmdArgs();
            try
            {
                ArgParser.Parse(cmdArgs, args);
                if (cmdArgs.ShowUsage)
                {
                    cmdArgs.Usage(null);
                    return;
                }
            }
            catch (Exception exp)
            {
                cmdArgs.Usage(exp.Message);
                return;
            }

            bool successful = false;

            // Read the connection string
            string cs = ConfigurationManager.AppSettings["ConnectionString"];
            if (string.IsNullOrEmpty(cs))
            {
                Logger.WriteError("The configuration key 'ConnectionString' is required!");
                goto END;
            }

            // Read tables/views.
            IList<Script> scripts = Scripts.GetManifest();

            // Init the logger.
            Logger.Init();
            try
            {
                // Checks whether the connection string is valid? 
                var csBuilder = new SqlConnectionStringBuilder(cs);
                if (!string.IsNullOrEmpty(cmdArgs.Server))
                {
                    csBuilder.DataSource = cmdArgs.Server;
                }

                if (string.IsNullOrEmpty(csBuilder.DataSource))
                {
                    Logger.WriteError("The data source is missing in the connection string.");
                    goto END;
                }
                if (string.IsNullOrEmpty(csBuilder.InitialCatalog))
                {
                    Logger.WriteError("The database name is missing in the connection string.");
                    goto END;
                }

                if (cmdArgs.ApplySP1)
                {
                    Logger.WriteLine("#");
                    Logger.WriteLine("# Be sure to BACKUP database {0} at {1} before applying service pack 1!", csBuilder.InitialCatalog, csBuilder.DataSource);
                    Logger.Write("# Are you sure to continue? [Y/N] ");
                    while (true)
                    {
                        ConsoleKeyInfo key = Console.ReadKey(true);
                        switch (key.KeyChar)
                        {
                            case 'y':
                            case 'Y':
                                Logger.WriteLine("Y");
                                goto APPLY_SP1;

                            case 'n':
                            case 'N':
                                // Cancelling the SP1
                                Logger.WriteLine("N");
                                goto END;

                            default:
                                Console.Beep();
                                break;
                        }
                    }

                APPLY_SP1:
                    Logger.WriteLine("");
                    Logger.WriteLine("Start to apply service pack 1 for database {0} at {1}...", csBuilder.InitialCatalog, csBuilder.DataSource);

                    using (var sqlConnection = new DBConnection(csBuilder.ConnectionString))
                    {
                        try
                        {
                            // Open the database connection and begin a transaction
                            sqlConnection.Open();
                            sqlConnection.BeginTransaction();

                            // Get the current time of the DB
                            DateTime currentTime = DBUtil.GetCurrentDBTime(sqlConnection);

                            // Drop existing tables/views?
                            Logger.WriteLine();
                            Logger.WriteLine("Updating DB schema...");
                            foreach (Script script in scripts)
                            {
                                Logger.WriteLine(string.IsNullOrEmpty(script.Description) ? script.Path : script.Description);
                                DBUtil.ExecuteEmbededResource(sqlConnection, GetScriptFullPath(script.Path));
                            }

                            Logger.WriteLine();
                            Logger.WriteLine("Updating stored procedures...");
                            var regex = new Regex(@"^UDS\.DB\.SP1\.SP.*\.(sproc_\w+)\.sql$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
                            DBUtil.ExecuteEmbededResources(sqlConnection, regex);

                            // Drop all the legacy stored procedures
                            DBUtil.DropAllStoredProcedures(sqlConnection, currentTime);

                            // Commit the transaction
                            sqlConnection.CommitTransaction();
                            successful = true;
                        }
                        catch (Exception exp)
                        {
                            Logger.LogException(exp);
                        }
                    }

                    Logger.WriteLine(null);
                    Logger.WriteLine(@"The DB was successfully created. Verify the 'NT AUTHORITY\NETWORK SERVICE' account has access permissions...");
                    DBUtil.VerifyNetworkServiceLogin(csBuilder);
                }
                else
                {
                    Logger.WriteLine("The service pack 1 won't be applied.");
                }
            }
            catch (Exception exp)
            {
                successful = false;
                Logger.LogException(exp);
            }

        END:
            // The end of the program.
            Logger.WriteLine();
            Logger.WriteLine(successful ? "Install succeeded!" : "Install failed!");
            Logger.WriteLine();
            Logger.Flush();
        }

        private static string GetScriptFullPath(string path)
        {
            path = path.Replace('/', '.').Replace('\\', '.');
            return BaseDir + path;
        }
    }
}
